#include "graphClass.h"
#include "winClassMain.h"
#include "resource.h"
#include <string>
#include <cmath>
#include <commctrl.h>
#include <sstream>
#include <float.h>

#define ID_DRW_WND 100 
#define ID_PANEL 101
#define ID_CONTROL 102
#define ID_YAXIS 103
#define ID_XSCALEMINUS 104	
#define ID_XSCALEPLUS 105
#define ID_XCOORDS 10006

const double stepXScale=100;
const int maxGraphOffset=9999;

//sizes of various interface elements
const int bottom_height=100;
const int drw_height=400;
int panel_width=150;
int grControlHeigth;
//

list<graphClass> graphs;

//scrollbars' info
SCROLLINFO vsi;
SCROLLINFO hsiDraw;
//

//logo bitmap
HBITMAP deadCrow;
//

//---------------axis
const int xAxisHeight=30;
const int yAxisWidth=75;
const int strokeLong=13;
const int strokeShort=7;
const int xAxisDivStandart=10;    //=min    
const int xAxisDivCount=10;    //count of divisions in step. 
const int yAxisStepStandart=40;    //=min 
const COLORREF axisBGColor=RGB(160,160,160);
//----------------------------

bool autoScroll = true;
bool autoScale = true;
double windowXScale=stepXScale;
int xAxisDiv=xAxisDivStandart;    //terminology: step is "big"(with text) and div (division) - "little" (as on a ruler mm-division, cm-step )
int yAxisStep=yAxisStepStandart;    
int windowMaxXPos = 0;
graphClass* chosenGraph;    //[temporary] for y axis. 
bool mtracking=FALSE;
bool scaleMin=false;    
const UINT defYInterfaceOffset = 30;    //then all x(pix),y(pix), etc., etc. should be UINT too

bool addGraph(graphClass gc);
void scrlset(HWND hWnd);
void scrlinit(HWND hWnd);
void scrlsetDraw(HWND hWnd);
void DrawMarker(HDC hDC, int height, int x);
void DrawXAxis(HDC hDC, int indMin, int x0, int width, int height);
void DrawXGrid(HDC hDC, int indMin, int x0, int width, int height, COLORREF color);	
void CorrectionXScaleAxis(double& xScale, int& xDiv);
int CorrectionYAxis(const double& yScale);
void SetIcon(HWND hWnd, HINSTANCE hInstance, WORD id);
HWND ToolbarInit(HWND hWnd, HINSTANCE hInstance);
template< typename T >
std::wstring toWstring(T& val, wstring init, wstring fin);
template< typename T >
std::wstring toWstring(T& val, wstring fin);
template< typename T >
std::wstring toWstring(wstring init, T& val);
BOOL CALLBACK EnumChildProc(HWND hChild, LPARAM lParam);
LRESULT CALLBACK WndProc(HWND hWnd,UINT msg,WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK WndProcDrawingWindow(HWND hWnd,UINT msg,WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK WndProcControlPanel(HWND hWnd,UINT msg,WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK WndProcGraphControl(HWND hWnd,UINT msg,WPARAM wParam, LPARAM lParam);
BOOL CALLBACK SourcesDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK InfoDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK WndProcGraphControlDlg(HWND hDlg,UINT msg,WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK WndProcControlPanelControl(HWND hWnd,UINT msg,WPARAM wParam, LPARAM lParam);
BOOL CALLBACK doubleBufferingProc(HWND hChild, LPARAM lParam);
BOOL CALLBACK yScaleButtonState(HWND hChild, LPARAM lParam);
LRESULT CALLBACK WndProcYAxis(HWND hWnd,UINT msg,WPARAM wParam, LPARAM lParam);


struct drwWndProperties {
	bool showVGrid;
	COLORREF bgColor;
	COLORREF gridColor;
};


int __stdcall WinMain(HINSTANCE hInstance,
					  HINSTANCE hPrevInstance,
					  LPSTR lpCmdLine, int nCmdShow)
{
	winClassMain wcmDrawingWindow = winClassMain(L"drawingWindow",WndProcDrawingWindow,hInstance,NULL,CS_HREDRAW|CS_VREDRAW|CS_OWNDC,(HBRUSH)COLOR_WINDOW+1);        
	winClassMain wcmControlPanel = winClassMain(L"controlPanel",WndProcControlPanel,hInstance,NULL,NULL,(HBRUSH)COLOR_WINDOW-1,0,4);
	winClassMain wcmGraphControl = winClassMain(L"graphControl",WndProcGraphControl,hInstance,NULL,NULL,(HBRUSH)COLOR_WINDOW,0,8);
	winClassMain wcm = winClassMain(L"myClass",WndProc,hInstance,MAKEINTRESOURCE(IDR_MENU1));

	winClassMain wcmYAxis=winClassMain(L"yAxis",WndProcYAxis,hInstance,NULL,CS_HREDRAW|CS_VREDRAW|CS_OWNDC,(HBRUSH)COLOR_WINDOW+1);   
	HWND hWnd = CreateWindow(L"myClass",L"GraphicZ",
		WS_OVERLAPPEDWINDOW|WS_VISIBLE|WS_CLIPCHILDREN|WS_THICKFRAME|WS_MINIMIZEBOX,
		150,100,950,600,
		NULL,NULL,hInstance,NULL);

	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);

	deadCrow=LoadBitmap(GetModuleHandle(NULL),MAKEINTRESOURCE(IDB_BITMAP1));

	HACCEL haccel=LoadAccelerators(hInstance,L"HKMenuAccel");	//for menu shortcuts

	MSG msg;

	while (true)
	{
		if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
		{
			if (msg.message == WM_QUIT)   break;
			if (!TranslateAccelerator(hWnd, haccel, &msg))
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}
	}
	return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd,UINT msg,WPARAM wParam, LPARAM lParam)
{
	static HMENU hMenu;
	static HINSTANCE hInstance;
	static HDC hDC;
	static HDC hDCBuffer;
	RECT parentRect;       
	static HWND hXCoords;
	static HWND hWndDrawingWindow;  
	static HWND hWndControlPanel;
	static HWND hWndYAxis;
	static MINMAXINFO* pmminfo;
	static TOOLINFO tbToolInfo;
	static HWND hWndToolbar;
	LPTOOLTIPTEXT lpTTT;

	static drwWndProperties dwProperties;		
	static CHOOSECOLOR cc;	//color dialog
	static COLORREF acrCustClr[16];  


	switch(msg)
	{          
	case WM_CREATE:
		{
			hInstance = GetModuleHandle(NULL);   
			RECT grControlRECT;
			HWND temp = CreateDialog((HINSTANCE)GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), hWnd, SourcesDlgProc);
			GetClientRect(temp,&grControlRECT);
			panel_width = grControlRECT.right-grControlRECT.left+19;				
			DestroyWindow(temp);
			grControlHeigth = grControlRECT.bottom - grControlRECT.top+2;

			RECT parentRect;      
			GetClientRect(hWnd, &parentRect);  
			hWndDrawingWindow = CreateWindow(L"drawingWindow",NULL,    WS_VISIBLE|WS_CHILD|WS_HSCROLL|WS_CLIPCHILDREN,
				10+yAxisWidth,defYInterfaceOffset,parentRect.right-parentRect.left-panel_width-40, parentRect.bottom-parentRect.top-bottom_height,
				hWnd,(HMENU)ID_DRW_WND,hInstance,NULL);
			//creating global graphics controls
			HWND globalGrButton1 = CreateWindow(L"BUTTON",L"+",WS_CHILD|BS_PUSHBUTTON,
				35,parentRect.bottom-parentRect.top-70,20,20,hWnd,(HMENU)ID_XSCALEPLUS,hInstance,NULL);
			HWND globalGrButton2 = CreateWindow(L"BUTTON",L"-",WS_CHILD|BS_PUSHBUTTON,
				10,parentRect.bottom-parentRect.top-70,20,20,hWnd,(HMENU)ID_XSCALEMINUS,hInstance,NULL);
			//creating control panel
			hWndControlPanel = CreateWindow(L"controlPanel",NULL,
				WS_VISIBLE|WS_CHILD|WS_CLIPCHILDREN|WS_VSCROLL,
				parentRect.right-panel_width,defYInterfaceOffset,panel_width,drw_height,
				hWnd,(HMENU)ID_PANEL,hInstance,NULL);
			scrlset(hWndControlPanel);
			//setting individual graphic controls       
			hXCoords = CreateWindow(L"STATIC",L"X = ", WS_CHILD|WS_VISIBLE|SS_LEFT,
				70,parentRect.bottom-parentRect.top-70,200,50,hWnd,(HMENU)ID_XCOORDS,hInstance,NULL);
			//y axis
			hWndYAxis = CreateWindow(L"yAxis",NULL,    WS_VISIBLE|WS_CHILD|WS_CLIPCHILDREN,    //WS_CLIPCHILDREN?
				10,defYInterfaceOffset,yAxisWidth, parentRect.bottom-parentRect.top-bottom_height,
				hWnd,(HMENU)ID_YAXIS,hInstance,NULL);

			hWndToolbar = ToolbarInit(hWnd, hInstance);
			SetIcon(hWnd, hInstance, IDI_ICONMAIN);			

			//color dialog
			cc.lStructSize = sizeof(CHOOSECOLOR);
			cc.hwndOwner = hWnd;
			cc.lpCustColors = (LPDWORD) acrCustClr;				
			cc.Flags = CC_FULLOPEN | CC_RGBINIT;

			//drawing window properties 
			dwProperties.showVGrid=true;
			dwProperties.bgColor=0x000000;
			dwProperties.gridColor=0x4f4f4f;
			SendMessage(hWndDrawingWindow, WM_USER+120, NULL, (LPARAM) &dwProperties);		

			break;
		}

	case WM_SIZE:
		{
			GetClientRect(hWnd, &parentRect);              
			EnumChildWindows(hWnd, EnumChildProc,(LPARAM) &parentRect);
			break;
		}

	case WM_GETMINMAXINFO:
		pmminfo = (MINMAXINFO*)lParam;  
		(*pmminfo).ptMinTrackSize.x=panel_width+20+212;
		(*pmminfo).ptMinTrackSize.y=290;
		break;

	case WM_LBUTTONUP:
		if(mtracking) mtracking=FALSE;
		break;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDM_SOURCES:
			if(DialogBox(hInstance, MAKEINTRESOURCE(IDD_SOURCES), hWnd, SourcesDlgProc))
			{
				autoScroll = true;	//needed??
				windowMaxXPos = 0;
				SendMessage(hWndToolbar, TB_SETSTATE, IDM_CLEARSOURCES, TBSTATE_ENABLED);
				SendMessage(hWndToolbar, TB_SETSTATE, ID_PLAYING_STOPALL, TBSTATE_ENABLED);
				EnableMenuItem(GetMenu(hWnd),ID_PLAYING_STOPALL,MF_BYCOMMAND);
				//for autoScroll true
				SendMessage(hWndToolbar, TB_SETSTATE, ID_PLAYING_AUTOSCROLLING, TBSTATE_CHECKED | TBSTATE_ENABLED);
				CheckMenuItem(GetMenu(hWnd), ID_PLAYING_AUTOSCROLLING, MF_BYCOMMAND|MF_CHECKED);
				SendMessage(hWnd,(WM_USER+99),NULL,NULL);	
				EnumChildWindows(hWndControlPanel,yScaleButtonState,NULL);
				InvalidateRect(hWndYAxis,NULL,TRUE);               
				InvalidateRect(hWndDrawingWindow,NULL,TRUE);

			}
			break;
		case IDM_CLEARSOURCES:
			{
				graphs.clear();

				SendMessage(hWndToolbar, TB_SETSTATE, IDM_CLEARSOURCES, TBSTATE_INDETERMINATE);
				SendMessage(hWndToolbar, TB_SETSTATE, ID_PLAYING_STOPALL, TBSTATE_INDETERMINATE);
				EnableMenuItem(GetMenu(hWnd),ID_PLAYING_STOPALL,MF_BYCOMMAND|MF_GRAYED);

				SendMessage(hWnd,(WM_USER+100),NULL,NULL);
				autoScroll = true;
				windowMaxXPos = 0;
				windowXScale=stepXScale;
				SendMessage(hWnd,(WM_USER+99),NULL,NULL);
			}
			break;
		case IDM_EXIT:
			{
				SendMessage(hWnd,WM_CLOSE,NULL,NULL);
			}
			break;
		case IDM_ABOUT:
			{
				DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG2), hWnd, AboutDlgProc);
			}
			break;
		case ID_XSCALEMINUS:   
			{
				double tmp=windowXScale*2;   
				if(tmp>=1e+6) break;    
				windowXScale=tmp;
				CorrectionXScaleAxis(windowXScale, xAxisDiv);    //for ANY way of setting scale
				scaleMin=true;  
				SendMessage(hWnd,(WM_USER+100),NULL,NULL);                              

			}
			break;    
		case ID_XSCALEPLUS:   
			{
				double tmp=windowXScale/2;
				if(tmp<=0.01) break;    //somewhere around 1000pix - 10ms
				windowXScale=tmp;
				CorrectionXScaleAxis(windowXScale, xAxisDiv);    //for ANY way of setting scale
				scaleMin=true;  
				SendMessage(hWnd,(WM_USER+100),NULL,NULL); 
			}
			break;
		case ID_PLAYING_AUTOSCROLLING:
			{
				autoScroll = !autoScroll;
				if(autoScroll)
				{
					CheckMenuItem(GetMenu(hWnd), ID_PLAYING_AUTOSCROLLING, MF_BYCOMMAND|MF_CHECKED);
					SendMessage(hWndToolbar, TB_SETSTATE, ID_PLAYING_AUTOSCROLLING, TBSTATE_ENABLED | TBSTATE_CHECKED);
				}
				else
				{
					CheckMenuItem(GetMenu(hWnd), ID_PLAYING_AUTOSCROLLING, MF_BYCOMMAND|MF_UNCHECKED);
					SendMessage(hWndToolbar, TB_SETSTATE, ID_PLAYING_AUTOSCROLLING, TBSTATE_ENABLED);
					SendMessage(hWnd,(WM_USER+100),NULL,NULL);                                       
				}
			}
			break;
		case ID_PLAYING_AUTOSCALING:
			{
				autoScale = !autoScale;
				EnumChildWindows(hWndControlPanel,yScaleButtonState,NULL);
				if(autoScale)
				{
					CheckMenuItem(GetMenu(hWnd), ID_PLAYING_AUTOSCALING, MF_BYCOMMAND|MF_CHECKED);						
					SendMessage(hWndToolbar, TB_SETSTATE, ID_PLAYING_AUTOSCALING, TBSTATE_ENABLED | TBSTATE_CHECKED);						
					SendMessage(hWnd,(WM_USER+100),NULL,NULL);
				}
				else
				{
					CheckMenuItem(GetMenu(hWnd), ID_PLAYING_AUTOSCALING, MF_BYCOMMAND|MF_UNCHECKED);
					SendMessage(hWndToolbar, TB_SETSTATE, ID_PLAYING_AUTOSCALING, TBSTATE_ENABLED);
				}
			}
			break;
		case ID_PLAYING_STOPALL:
			{
				for (list<graphClass>::iterator ii = graphs.begin();ii!=graphs.end();++ii)
				{
					ii->stop();
				}
				SendMessage(hWnd,(WM_USER+99),NULL,NULL);
				EnableMenuItem(GetMenu(hWnd),ID_PLAYING_STOPALL,MF_BYCOMMAND|MF_GRAYED);
				SendMessage(hWndToolbar, TB_SETSTATE, ID_PLAYING_STOPALL, TBSTATE_INDETERMINATE);
			}
			break;
		case ID_VERTICALGRID:
			{			
				dwProperties.showVGrid=!dwProperties.showVGrid;	
				if(dwProperties.showVGrid) CheckMenuItem(GetMenu(hWnd), ID_VERTICALGRID, MF_BYCOMMAND|MF_CHECKED);
				else CheckMenuItem(GetMenu(hWnd), ID_VERTICALGRID, MF_BYCOMMAND|MF_UNCHECKED);
				SendMessage(hWndDrawingWindow, WM_USER+120, NULL, (LPARAM)&dwProperties);		
			}
			break;
		case ID_BGCOLOR:
			{
				if (ChooseColor(&cc)) 
				{
					dwProperties.bgColor = cc.rgbResult;					
					SendMessage(hWndDrawingWindow, WM_USER+120, NULL, (LPARAM) &dwProperties);	
				}
			}
			break;		
		case ID_GRIDCOLOR:
			{
				if (ChooseColor(&cc)) 
				{
					dwProperties.gridColor = cc.rgbResult;					
					SendMessage(hWndDrawingWindow, WM_USER+120, NULL, (LPARAM) &dwProperties);	
				}
			}
			break;		
		default:
			break;
		}       
		break;

	case WM_NOTIFY:			//for tooltips
		lpTTT = (LPTOOLTIPTEXT)lParam;
		if (lpTTT->hdr.code == TTN_NEEDTEXT) {
			lpTTT->hinst = GetModuleHandle(NULL);
			lpTTT->lpszText = MAKEINTRESOURCE(lpTTT->hdr.idFrom);
		}
		break;

	case (WM_USER+101):
		SetWindowText(hXCoords,(LPCWSTR)lParam);
		break;

	case (WM_USER+102):
		if(chosenGraph) yAxisStep=CorrectionYAxis(chosenGraph->getYScale());
		InvalidateRect(hWndYAxis,NULL,TRUE);
		break;

	case (WM_USER+100):
		InvalidateRect(hWndDrawingWindow,NULL,TRUE);
		break;
	case (WM_USER+99):
		{
			HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
			DestroyWindow(hWndControlPanel);
			RECT parentRect;
			GetClientRect(hWnd, &parentRect);

			hWndControlPanel = CreateWindow(L"controlPanel",NULL,
				WS_VISIBLE|WS_CHILD|WS_CLIPCHILDREN|WS_VSCROLL,
				parentRect.right-panel_width,defYInterfaceOffset,panel_width,drw_height,
				hWnd,(HMENU)ID_PANEL,hInstance,NULL);
			ShowWindow(hWndControlPanel, SW_SHOW);
			UpdateWindow(hWndControlPanel);
			scrlset(hWndControlPanel);
			break;
		}

	case WM_CLOSE:
		DestroyWindow(hWnd);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd,msg,wParam,lParam);
	}
	return 0;
}


BOOL CALLBACK SourcesDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)    //list horizontal scroll!
{
	static HWND hListBox;
	static OPENFILENAME ofn;
	static wchar_t szFile[MAX_PATH];
	int iCurItem;
	int listCount=0;
	wchar_t sfname[MAX_PATH];
	COLORREF colors[5]={RGB(255,0,0),RGB(0,250,0),RGB(130,130,255),RGB(170,20,60),RGB(255,255,9)};  //add more colors
	int kk;

	switch (uMsg) {
case WM_INITDIALOG:
	hListBox = GetDlgItem(hDlg, IDC_LIST_SOURCES);
	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner = hDlg;
	ofn.lpstrFile = szFile;
	ofn.lpstrFile[0] = '\0';
	ofn.lpstrFilter = L"All\0*.*\0txt\0*.TXT\0";
	ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
	ofn.nMaxFile = sizeof(szFile);
	return TRUE;

case WM_COMMAND:
	switch (LOWORD(wParam)) {
case IDC_SOURCES_ADD:
	if (GetOpenFileName(&ofn))
		SendMessage(hListBox, LB_ADDSTRING, 0,(LPARAM)ofn.lpstrFile);   
	break;
case IDC_SOURCES_REMOVE:
	iCurItem = SendMessage(hListBox, LB_GETCURSEL, 0, 0);
	if (iCurItem != LB_ERR) {
		SendMessage(hListBox, LB_DELETESTRING, iCurItem, 0);
	}
	break;
case IDOK:        //filling listGraphs here
	graphs.clear();//clearing old graphics

	//setting to default
	chosenGraph=NULL;
	//windowXScale=stepXScale;
	//xAxisDiv=xAxisDivStandart;    
	yAxisStep=yAxisStepStandart;    
	windowMaxXPos = 0;

	listCount=SendMessage(hListBox, LB_GETCOUNT, 0,0);
	kk=0;
	for(int i=0; i<listCount; i++)
	{
		SendMessage(hListBox, LB_GETTEXT, (LPARAM)i,(LPARAM)sfname);
		graphClass gc = graphClass(sfname, colors[kk%5], GetParent(hDlg));
		++kk;
		addGraph(gc);
	}
	EnableMenuItem(GetMenu(GetParent(hDlg)),ID_PLAYING_STOPALL,MF_BYCOMMAND|MF_ENABLED);
	EndDialog(hDlg, TRUE);
	return TRUE;
case IDCANCEL:
	EndDialog(hDlg, FALSE);
	return TRUE;
	}
	break;
	}
	return FALSE;
}


bool addGraph(graphClass gc)
{
	graphs.push_back(gc);
	return true;
}

BOOL CALLBACK AboutDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)   
{
	switch (uMsg) {
case WM_INITDIALOG:
	{
		SendMessage(GetDlgItem(hDlg, IDC_STATICPIC),STM_SETIMAGE,(WPARAM)IMAGE_BITMAP,(LPARAM)deadCrow);
		InvalidateRect(GetDlgItem(hDlg, IDC_STATICPIC),NULL,true);
	}
	break;
case WM_COMMAND:
	switch (LOWORD(wParam)) {
case IDOK:       
	EndDialog(hDlg, TRUE);
	return TRUE;
case IDCANCEL:
	EndDialog(hDlg, FALSE);
	return TRUE;
	}
	break;
	}
	return FALSE;
}

void scrlsetDraw(HWND hWnd)
{
	RECT drawRect;
	GetClientRect(hWnd, &drawRect);
	int maxLength = 0;
	for (list<graphClass>::iterator ii = graphs.begin();ii!=graphs.end();++ii)
	{
		if (maxLength < ii->getLength()*ii->getPeriod())
			maxLength = ii->getLength()*ii->getPeriod();
	}
	hsiDraw.cbSize=sizeof(hsiDraw);
	hsiDraw.fMask = SIF_RANGE|SIF_PAGE|SIF_POS|SIF_DISABLENOSCROLL;
	hsiDraw.nPage = drawRect.right-drawRect.left;
	hsiDraw.nMax = maxLength/windowXScale;
	if (hsiDraw.nPage > hsiDraw.nMax)
	{
		hsiDraw.nPos = hsiDraw.nMin;
	}
	else
	{
		if(autoScroll  || (scaleMin && hsiDraw.nPos>=hsiDraw.nMax/2))   //scaleMin only when graph stopped or on less than half
		{
			hsiDraw.nPos = hsiDraw.nMax - hsiDraw.nPage;
			if(scaleMin) scaleMin=false;   
		}
	}
	SetScrollInfo(hWnd, SB_HORZ, &hsiDraw, TRUE);
}
void autoScrollOff(HWND hWnd)
{
	autoScroll = false;
	CheckMenuItem(GetMenu(hWnd), ID_PLAYING_AUTOSCROLLING, MF_BYCOMMAND|MF_UNCHECKED);
}

LRESULT CALLBACK WndProcDrawingWindow(HWND hWnd,UINT msg,WPARAM wParam, LPARAM lParam)
{
	static int markerX=0;   //axis's center
	static drwWndProperties* p_dwProperties;	

	HWND hWndPar = GetParent(hWnd);
	switch(msg)
	{
		static int xInc;

	case WM_HSCROLL:
		switch(LOWORD(wParam))
		{
		case SB_LINELEFT:
			autoScrollOff(hWndPar);
			xInc = -1;
			break;
		case SB_LINERIGHT:
			autoScrollOff(hWndPar);
			xInc = 1;
			break;
		case SB_PAGELEFT:
			autoScrollOff(hWndPar);
			xInc = -(int)hsiDraw.nPage;
			break;
		case SB_PAGERIGHT:
			autoScrollOff(hWndPar);
			xInc = (int)hsiDraw.nPage;
			break;
		case SB_THUMBTRACK:
			autoScrollOff(hWndPar);
			xInc = HIWORD(wParam) - hsiDraw.nPos;
			break;
		default:
			xInc = 0;
			break;
		}

		hsiDraw.nPos += xInc;
		if (hsiDraw.nPos+hsiDraw.nPage>hsiDraw.nMax)
			hsiDraw.nPos=hsiDraw.nMax-hsiDraw.nPage;
		if (hsiDraw.nPos<hsiDraw.nMin)
			hsiDraw.nPos=hsiDraw.nMin;

		SetScrollInfo(hWnd,SB_HORZ,&hsiDraw,TRUE);
		RECT drawRect;
		GetClientRect(hWnd, &drawRect);
		InvalidateRect(hWnd, &drawRect, FALSE);
		return 0;
	case WM_LBUTTONDOWN:
		if(LOWORD(lParam)>=markerX-2 && LOWORD(lParam)<=markerX+2) mtracking=TRUE;     
		break;
	case WM_LBUTTONUP:
		if(mtracking) mtracking=FALSE;
		break;
	case WM_MOUSEMOVE:
		if(mtracking)
		{
			markerX = LOWORD(lParam);
			InvalidateRect(hWnd,NULL,TRUE);                
		}

		break;
	case WM_ERASEBKGND:
		return (LRESULT)1;

	case WM_USER+120:		//setting window properties
		{	
			p_dwProperties=(drwWndProperties*)lParam;	//lParam contains pointer to properties struct object, which is a stsic variable of main window 
			InvalidateRect(hWnd, NULL, TRUE);	
		}
		break;

	case WM_PAINT:
		{
			scrlsetDraw(hWnd);
			PAINTSTRUCT ps;
			RECT drawRect;
			GetClientRect(hWnd, &drawRect);
			HDC hDC = BeginPaint(hWnd,&ps);
			int width=drawRect.right-drawRect.left;        
			int heightFull=drawRect.bottom-drawRect.top;
			int heightGraph=heightFull-xAxisHeight;    

			//////////////////////////////
			HDC hDCBuffer = CreateCompatibleDC(hDC);    //permanent bitmap needed?
			HBITMAP hBitmapBuffer = CreateCompatibleBitmap(hDC,width, heightFull);
			SelectObject(hDCBuffer, hBitmapBuffer);

			//filling background for graphs; for x axis later (after graphs are drawn) 
			drawRect.bottom-=xAxisHeight;
			LOGBRUSH br;
			br.lbStyle = BS_SOLID;
			br.lbColor = p_dwProperties->bgColor;			
			HBRUSH brush;
			brush = CreateBrushIndirect(&br);
			FillRect(hDCBuffer, &drawRect, brush);        
			DeleteObject(brush);

			//for x axis and x grid
			int indAxMin=hsiDraw.nPos/xAxisDiv-xAxisDivCount;
			int x0=indAxMin*xAxisDiv-hsiDraw.nPos;
			if (p_dwProperties->showVGrid) DrawXGrid(hDCBuffer, indAxMin, x0, width, heightGraph, p_dwProperties->gridColor);    

			/////////////////////////////////
			bool maxValChanged=false;    //for redrawing y axis
			int maxLength = 0;
			int startX;
			int k = 0;
			int period=0;
			int leng=0;
			double maxValue = 0;
			bool skipXactive=false;
			for (list<graphClass>::iterator ii = graphs.begin();ii!=graphs.end();++ii)
			{
				period=ii->getPeriod();
				leng=ii->getLength();
				if (maxLength < leng*period)
					maxLength = leng*period;
				double maxGraphValue = (abs(ii->getMinValue()*ii->getYScale()+ii->getYOffset()*ii->getYScale())>abs(ii->getMaxValue()*ii->getYScale()+ii->getYOffset()*ii->getYScale()))?abs(ii->getMinValue()*ii->getYScale()+ii->getYOffset()*ii->getYScale()):abs(ii->getMaxValue()*ii->getYScale()+ii->getYOffset()*ii->getYScale());
				if (maxValue < maxGraphValue)
				{
					maxValue = maxGraphValue;
					maxValChanged=true;
				}
				if(windowXScale/period>=0.1)   
					skipXactive=true;
			}
			double graphScaleChange = 1;
			if (autoScale)
			{
				graphScaleChange = heightGraph/2/maxValue;
				if (!(graphScaleChange <= DBL_MAX && graphScaleChange >= -DBL_MAX))
				{
					graphScaleChange = 1;
				}
			}
			windowMaxXPos = hsiDraw.nPos;
			windowMaxXPos += hsiDraw.nPage;
			int pixXMax = windowMaxXPos;
			int pixXMin = pixXMax - width;
			int skip=0, stepX=0, k_skip=0, k_gdc;           //skipping not drawing, but only x+=
			if(skipXactive)
			{
				skip=floor(windowXScale/1+0.5)-1;
				stepX=(skip>=1) ? 1 : floor(1/windowXScale+0.5);        
			}

			//-------------COORD LABELS------------------------------------------
			int timeMs=(hsiDraw.nPos+markerX)*windowXScale;    
			double time=0.001*timeMs;      

			int inext=0, iprev=0;
			double pos=0.0, valPrev=0, valNext=0, val=0;
			HWND hWndT;
			//-----------------------------------------------------------------------
			for (list<graphClass>::iterator ii = graphs.begin();ii!=graphs.end();++ii)
			{
				ii->setYScale(ii->getYScale()*graphScaleChange);
				period = ii->getPeriod();
				leng = ii->getLength();                            

				if (ii->isVisible())
				{                                      
					if (leng)
					{
						HPEN hPen = CreatePen(PS_SOLID, 1, ii->getColor());
						SelectObject( hDCBuffer, hPen );
						int indXMax =((pixXMax*windowXScale/period+1) < leng)?(pixXMax*windowXScale/period+1):(leng-1); //getting max index to draw     //////////
						int indXMin = ((pixXMin*windowXScale/period-1) > 0)?(pixXMin*windowXScale/period-1):0;
						int drawXMin = indXMin*period/windowXScale - pixXMin;    

						int c = 0;
						int x=drawXMin;
						MoveToEx(hDCBuffer,x,heightGraph/2,NULL);

						if(skipXactive)
						{
							k_skip=0;
							k_gdc=0;
							int j=indXMin;
							while(1)
							{
								if(j>indXMax) break;
								if(skip>=1)             //same for all graphs
								{
									if(k_skip==skip) {      k_skip=0; x+=stepX; }
									else ++k_skip;
								}
								else x+=stepX;

								if(k_gdc*1==period)
								{
									c = floor((ii->getValue(j)*ii->getYScale())+0.5+ii->getYOffset()*ii->getYScale());
									LineTo(hDCBuffer,x,heightGraph/2-c);
									k_gdc=0;
									++j;
								}
								else  ++k_gdc;
							}
						}
						else
						{
							for (int j = indXMin;j<=indXMax;j++)   
							{
								c = floor((ii->getValue(j)*ii->getYScale())+0.5+ii->getYOffset()*ii->getYScale()
									);
								x+=floor(period/windowXScale+0.5);
								LineTo(hDCBuffer,x,heightGraph/2-c);
							}
						}
						DeleteObject( SelectObject( hDCBuffer, GetStockObject( BLACK_PEN ) ));
					}      
				}

				//Y coords by marker position
				pos=(double)timeMs/(double)period-1; //cause starts from 0, though in file(and vector) it doesn't
				inext=ceil(pos);
				iprev=floor(pos);
				if(iprev>=0 && iprev<leng) valPrev=ii->getValue(iprev); 
				else valPrev=0;
				if(inext>=0 && inext<leng) valNext=ii->getValue(inext);
				else valNext=valPrev;

				val=valNext;
				if(inext!=iprev) val=valPrev+(valNext-valPrev)*(pos-iprev)/(inext-iprev);                               

				hWndT = GetDlgItem(ii->getHCtrl(), IDC_STATIC_Y);
				if (hWndT)
				{           
					if(hsiDraw.nPos+markerX>leng*period/windowXScale ||  period/windowXScale<1) SetWindowText(hWndT,L"Y = ");   
					else SetWindowText(hWndT,(toWstring(L"Y = ", val)).c_str());   
				}
			}
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			DrawXAxis(hDCBuffer, indAxMin, x0, width, heightFull);
			DrawMarker(hDCBuffer, heightGraph, markerX);  
			///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////        
			SetStretchBltMode(hDC, COLORONCOLOR);
			BitBlt(hDC, 0, 0, width, heightFull,    
				hDCBuffer, 0, 0, SRCCOPY);
			EndPaint(hWnd,&ps);
			DeleteDC(hDCBuffer);
			DeleteObject(hBitmapBuffer);
			hDCBuffer = NULL;
			/////////////////////////
			SendMessage(hWndPar, WM_USER+101, NULL, (LPARAM)((toWstring(time,L"X = ", L" sec")).c_str()));    
			if(chosenGraph!=NULL && maxValChanged && autoScale)
			{
				PostMessage(GetParent(hWnd),WM_USER+102,NULL,NULL);
			}
		}
		return 0;
	default:
		return DefWindowProc(hWnd,msg,wParam,lParam);
	}
	return 0;
}

void DrawMarker(HDC hDC, int height, int x)     //pos is centerX    
{
	POINT pt[3];
	pt[0].x=x-5; pt[0].y=0;
	pt[1].x=x+5; pt[1].y=0;
	pt[2].x=x; pt[2].y=10;
	HBRUSH brush = CreateSolidBrush(RGB(250, 225, 134));
	SelectObject(hDC, brush);
	Polygon(hDC, pt, 3);
	pt[0].y=height;
	pt[1].y=height;
	pt[2].y=height-10;
	Polygon(hDC, pt, 3);
	RECT rect;
	rect.top=0; rect.left=x-1; rect.bottom=height; rect.right=x+1;  //width=3;
	FillRect(hDC, &rect, brush);
	DeleteObject(brush);
}

void DrawXGrid(HDC hDC, int indMin, int x0, int width, int height, COLORREF color)    //heightGraph
{
	int indMax=indMin+width/xAxisDiv+1+xAxisDivCount;
	HPEN hPen = CreatePen(PS_SOLID, 1, color);  
	SelectObject( hDC, hPen );
	int x=x0;
	for(int i=indMin; i<=indMax; i++)        //vert grid
	{        
		if(i%xAxisDivCount==0)    
		{
			MoveToEx(hDC,x,0,NULL);
			LineTo(hDC,x,height);            
		}
		x+=xAxisDiv;    
	}
	DeleteObject(hPen);
}

void DrawXAxis(HDC hDC, int indMin, int x0, int width, int height)    //height of all drawing window
{    
	RECT rect;    //filling background for x axis
	rect.top=height-xAxisHeight;
	rect.bottom=height;
	rect.left=0;
	rect.right=width;
	LOGBRUSH br;    
	br.lbStyle = BS_SOLID;
	br.lbColor = 0x000000;
	HBRUSH brush;
	br.lbStyle = BS_SOLID;
	br.lbColor = axisBGColor;            
	brush = CreateBrushIndirect(&br);
	FillRect(hDC, &rect, brush);    
	DeleteObject(brush);

	int x=x0;
	int indMax=indMin+width/xAxisDiv+1+xAxisDivCount;
	double temp=0;
	bool middleStroke =(xAxisDivCount%2==0);        
	wstring wstr;
	HPEN hPen = CreatePen(PS_SOLID, 1, 0x000000);
	SelectObject( hDC, hPen );
	SetBkColor(hDC,axisBGColor);

	for(int i=indMin; i<=indMax; i++)    //axis
	{        
		MoveToEx(hDC,x,rect.top,NULL);
		if(i%xAxisDivCount==0)
		{
			LineTo(hDC,x,rect.top+strokeLong);
			temp=i*xAxisDiv*windowXScale/1000;
			wstr=toWstring(temp,L" s");
			TextOut(hDC, x, rect.top+strokeLong, wstr.c_str(), wstr.size());
		}
		else if(middleStroke && i%(xAxisDivCount>>1)==0)    LineTo(hDC,x,rect.top+strokeLong);   
		else LineTo(hDC,x,rect.top+strokeShort);
		x+=xAxisDiv;    
	}    
}

void scrlinit(HWND hWnd)
{
	vsi.cbSize=sizeof(vsi);
	vsi.fMask = SIF_RANGE|SIF_PAGE|SIF_POS|SIF_DISABLENOSCROLL;
	RECT panelRect;
	GetWindowRect(hWnd, &panelRect);
	int height=panelRect.bottom-panelRect.top;
	vsi.nPage = height;
	vsi.nMax = grControlHeigth*graphs.size()-(graphs.size()-1);
	int nPosTemp = vsi.nPos;
	if (vsi.nPage > vsi.nMax)
	{
		vsi.nPos = vsi.nMin;
	}
	else
	{
		if (vsi.nPos+vsi.nPage>vsi.nMax)
			vsi.nPos=vsi.nMax-vsi.nPage+1;
		if (vsi.nPos<vsi.nMin)
			vsi.nPos=vsi.nMin;
	}	
	ScrollWindow(hWnd,0, - vsi.nPos,NULL,NULL);
	SetScrollInfo(hWnd, SB_VERT, &vsi, TRUE);
}
void scrlset(HWND hWnd)
{
	vsi.cbSize=sizeof(vsi);
	vsi.fMask = SIF_RANGE|SIF_PAGE|SIF_POS|SIF_DISABLENOSCROLL;
	RECT panelRect;
	GetWindowRect(hWnd, &panelRect);
	int height=panelRect.bottom-panelRect.top;
	vsi.nPage = height;
	vsi.nMax = grControlHeigth*graphs.size()-(graphs.size()-1);
	int nPosTemp = vsi.nPos;
	if (vsi.nPage > vsi.nMax)
	{
		vsi.nPos = vsi.nMin;
	}
	else
	{
		if (vsi.nPos+vsi.nPage>vsi.nMax)
			vsi.nPos=vsi.nMax-vsi.nPage+1;
		if (vsi.nPos<vsi.nMin)
			vsi.nPos=vsi.nMin;
	}
	ScrollWindow(hWnd,0,nPosTemp - vsi.nPos,NULL,NULL);
	SetScrollInfo(hWnd, SB_VERT, &vsi, TRUE);
}

LRESULT CALLBACK WndProcControlPanel(HWND hWnd,UINT msg,WPARAM wParam, LPARAM lParam)
{
	static int yInc;
	bool b;
	int nPosTemp;
	switch(msg)
	{
	case WM_CREATE:
		{
			HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
			SetWindowLong(hWnd, 0, (LONG)-1);	//this is the number of chosen graphic!
			int amount = graphs.size();
			int index = 0;
			list<graphClass>::iterator ii = graphs.begin();
			for (int i=0;i<amount;i++)
			{
				HWND grControl = CreateWindow(L"graphControl",NULL,
					WS_CHILD|WS_CLIPCHILDREN,
					0,grControlHeigth*i-i,panel_width,grControlHeigth,
					hWnd,(HMENU)ID_CONTROL,hInstance,NULL);
				SetWindowLong(grControl, 0, (LONG)((graphClass*)&(*(ii))));
				SetWindowLong(grControl, 4, (LONG)index);
				HWND grControlDlg = CreateDialog((HINSTANCE)GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), grControl, WndProcGraphControlDlg);
				ShowWindow(grControl, SW_SHOW);
				ii->setHCtrl(grControlDlg);
				++index;
				++ii;	
			}
		}
		break;

	case WM_LBUTTONUP:		
		if(mtracking) mtracking=FALSE;
		break;

	case WM_VSCROLL:
		switch(LOWORD(wParam))
		{
		case SB_LINEUP:
			yInc = -1;
			break;
		case SB_LINEDOWN:
			yInc = 1;
			break;
		case SB_PAGEUP:
			yInc = -(int)vsi.nPage;
			break;
		case SB_PAGEDOWN:
			yInc = (int)vsi.nPage;
			break;
		case SB_THUMBTRACK:
			yInc = HIWORD(wParam) - vsi.nPos;
			break;
		default:
			yInc = 0;
			break;
		}
		nPosTemp = vsi.nPos;
		vsi.nPos += yInc;		
		if (vsi.nPos+vsi.nPage>vsi.nMax)
			vsi.nPos=vsi.nMax-vsi.nPage+1;
		if (vsi.nPos<vsi.nMin)
			vsi.nPos=vsi.nMin;
		yInc = vsi.nPos - nPosTemp;
		ScrollWindow(hWnd,0,-yInc,NULL,NULL);
		SetScrollInfo(hWnd,SB_VERT,&vsi,TRUE);
		return 0;
	default:
		return DefWindowProc(hWnd,msg,wParam,lParam);
	}
}

//this procedure handles drawing of various controls implementing double-buffering
//control should have its default window procedure(which is not DefWindowProc btw) stored in USERDATA field
LRESULT CALLBACK WndProcControlPanelControl(HWND hWnd,UINT msg,WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
	case WM_ERASEBKGND:
		return (LRESULT)1;
	case WM_PAINT:
		{			
			PAINTSTRUCT ps;
			RECT drawRect;
			GetClientRect(hWnd, &drawRect);
			HDC hDC = BeginPaint(hWnd,&ps);
			SetBkColor(hDC,((graphClass*)GetWindowLong(GetParent(GetParent(hWnd)),0))->getColor());
			int width=drawRect.right-drawRect.left;
			int height=drawRect.bottom-drawRect.top;
			HDC hDCBuffer = CreateCompatibleDC(hDC);
			HBITMAP hBitmapBuffer = CreateCompatibleBitmap(hDC,width, height);
			SelectObject(hDCBuffer, hBitmapBuffer);           
			LOGBRUSH br;
			br.lbStyle = BS_SOLID;
			br.lbColor = 0x000000;
			HBRUSH brush;
			brush = CreateBrushIndirect(&br);
			FillRect(hDCBuffer, &drawRect, brush);
			DeleteObject(brush);

			CallWindowProc((WNDPROC)GetWindowLong(hWnd, GWL_USERDATA), hWnd, msg, WPARAM(hDCBuffer), lParam);
			//copying
			SetStretchBltMode(hDC, COLORONCOLOR);
			BitBlt(hDC, 0, 0, width, height, hDCBuffer, 0, 0, SRCCOPY);
			////////////////////////
			EndPaint(hWnd,&ps);  
			//////////////////////
			DeleteDC(hDCBuffer);
			DeleteObject(hBitmapBuffer);
			hDCBuffer = NULL; 			
			/////////////////////////  
			return 0;
		}
	default:
		return CallWindowProc((WNDPROC)GetWindowLong(hWnd, GWL_USERDATA), hWnd, msg, wParam, lParam);		
	}
}
//this function automatically reassigns window procedure and stores old one in USERDATA field for all childs of whatever control
//also it sets WS_EX_COMPOSITE style but i doubt its efficiency actually
BOOL CALLBACK doubleBufferingProc(HWND hChild, LPARAM lParam)
{
	LONG oldProc = GetWindowLong(hChild, GWL_WNDPROC);
	SetWindowLong(hChild, GWL_USERDATA, oldProc);
	SetWindowLong(hChild, GWL_WNDPROC, (LONG)WndProcControlPanelControl);
	LONG oldStyleEx = GetWindowLong(hChild, GWL_EXSTYLE);
	SetWindowLong(hChild, GWL_EXSTYLE, oldStyleEx|WS_EX_COMPOSITED);
	LONG oldStyle = GetWindowLong(hChild, GWL_STYLE);
	SetWindowLong(hChild, GWL_STYLE, oldStyle|WS_CLIPCHILDREN);
	return TRUE;
}
//this function handles the placement of graphic controls when one graphic has been deleted
BOOL CALLBACK EnumChildPanelPlacement(HWND hChild, LPARAM lParam)
{
	int idChild = GetWindowLong(hChild, GWL_ID);
	if (idChild == ID_CONTROL)
	{
		int index = (int)GetWindowLong(hChild, 4);
		if (index > (int)lParam)
		{
			index--;
			SetWindowLong(hChild, 4, (LONG)index);
		}
		int xPosition = 0;
		int yPosition = grControlHeigth*index-index;
		SetWindowPos(hChild, HWND_TOP,xPosition,yPosition,0,0,SWP_SHOWWINDOW|SWP_NOSIZE);

	}
	return TRUE;
}
//function for redrawing all graphic controls, used after new graphic has been choosen
BOOL CALLBACK EnumChildRedraw(HWND hChild, LPARAM lParam)
{
	InvalidateRect(hChild,NULL,TRUE);
	return TRUE;
}
//graphic's info dialog procedure
BOOL CALLBACK InfoDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{

	switch (uMsg) {
case WM_INITDIALOG:
	{
		int k = (int)lParam;//and here we set the values of static textfields: tbc...//may be min and max values should be setted dynamically?
		//graphic's name
		HWND grInfoDlgLabel = GetDlgItem(hDlg, IDCI_NAME);
		wstring grName = ((graphClass*)k)->getGraphName();
		wchar_t *grNameChar = (wchar_t *)grName.c_str();
		SendMessage(grInfoDlgLabel, WM_SETTEXT, NULL, (LPARAM)grNameChar);
		//path to graphic
		grInfoDlgLabel = GetDlgItem(hDlg, IDCI_PATH);
		grName = ((graphClass*)k)->getFilePath();
		grNameChar = (wchar_t *)grName.c_str();
		SendMessage(grInfoDlgLabel, WM_SETTEXT, NULL, (LPARAM)grNameChar);
		//period
		grInfoDlgLabel = GetDlgItem(hDlg, IDCI_PERIOD);
		int period = ((graphClass*)k)->getPeriod();
		SetWindowText(grInfoDlgLabel,(toWstring(period,L"", L" ms")).c_str());
		//max value
		grInfoDlgLabel = GetDlgItem(hDlg, IDCI_MAX);
		int max = ((graphClass*)k)->getMaxValue();
		SetWindowText(grInfoDlgLabel,(toWstring(max,L"", L"")).c_str());
		//min value
		grInfoDlgLabel = GetDlgItem(hDlg, IDCI_MIN);
		int min = ((graphClass*)k)->getMinValue();
		SetWindowText(grInfoDlgLabel,(toWstring(min,L"", L"")).c_str());
		//
		return false;
	}
case WM_COMMAND:
	switch (LOWORD(wParam)) 
	{
	case IDOK:       
		EndDialog(hDlg, TRUE);
		return TRUE;
	case IDCANCEL:
		EndDialog(hDlg, FALSE);
		return TRUE;
	}
	break;
	}
	return FALSE;
}
//function for disabling scaling buttons on graphic panel
BOOL CALLBACK yScaleButtonState(HWND hChild, LPARAM lParam)
{
	LONG idChild = GetWindowLong(hChild, GWL_ID);

	if ((idChild == IDC_BUTTONSCALEMINUS)||(idChild == IDC_BUTTONSCALEPLUS))
	{
		EnableWindow(hChild, !autoScale);
	}
	InvalidateRect(hChild,NULL,TRUE);
	return TRUE;
}

BOOL CALLBACK WndProcGraphControlDlg(HWND hDlg,UINT msg,WPARAM wParam, LPARAM lParam)
{
	static int yInc;
	//
	static int difX = 5;
	static int difY = 10; //this variables are the difference between button size and image size

	bool b;
	int k;
	static COLORREF custColors[16];//fill it with our default colours?
	HWND hWnd = GetParent(hDlg);
	k = GetWindowLong(hWnd, 0);
	switch(msg)
	{
	case WM_INITDIALOG:
		{
			if (k!=0)
			{
				//setting WS_CLIPCHILDREN style
				LONG oldStyle = GetWindowLong(hDlg, GWL_STYLE);
				SetWindowLong(hDlg, GWL_STYLE, oldStyle|WS_CLIPCHILDREN);
				//setting some stuff for double-buffering
				EnumChildWindows(hDlg, doubleBufferingProc, NULL);
				//Setting Spin Button range
				HWND grControlDlgSpin = GetDlgItem(hDlg, IDC_SPINOFFSET);
				SendMessage(grControlDlgSpin,UDM_SETRANGE,TRUE,MAKELPARAM(9999,-9999));
				//Setting label text
				HWND grControlDlgLabel = GetDlgItem(hDlg, IDC_LABEL);
				const wstring grName = ((graphClass*)k)->getGraphName();
				const wchar_t *grNameChar = (const wchar_t *)grName.c_str();
				SendMessage(grControlDlgLabel, WM_SETTEXT, NULL, (LPARAM)grNameChar);
				//Setting color button image
				HWND grControlDlgColor = GetDlgItem(hDlg, IDC_COLOR);				
				RECT drawRect;
				GetClientRect(grControlDlgColor, &drawRect);
				int width=drawRect.right-drawRect.left;
				int height=drawRect.bottom-drawRect.top;
				HDC hDC = GetDC(grControlDlgColor);
				HDC init = CreateCompatibleDC(hDC);
				HBITMAP hBitmap = CreateCompatibleBitmap(hDC,width-difX, height-difY);
				HBITMAP oldHBitmap = (HBITMAP)(SelectObject(init, hBitmap));
				HBRUSH brushRect = CreateSolidBrush(((graphClass*)k)->getColor());
				HBRUSH brushFrame = CreateSolidBrush(RGB(0,0,0));

				//
				RECT frameRect = drawRect;
				frameRect.bottom-=difY;
				frameRect.right-=difX;

				FillRect(init, &drawRect, brushRect);//filling coloured rectangle
				FrameRect(init, &frameRect, brushFrame);//adding cute frame around :P
				SelectObject( init, oldHBitmap );

				SendMessage(grControlDlgColor, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap);
				DeleteObject(brushRect);
				DeleteObject(brushFrame);
				ReleaseDC(grControlDlgColor,hDC);
				DeleteDC(init);
				init = NULL; 	
				return false;
			}
		}
	case WM_CTLCOLORBTN://won't work unless we have owner-drawn button
	case WM_CTLCOLORSTATIC:
	case WM_CTLCOLORDLG://setting highlighting for chosen one +) i heard i need to delete brush explicitly or it will eat my memory
		{
			if(GetWindowLong(hWnd,4) == GetWindowLong(GetParent(hWnd),0))
			{
				HDC  hdc  = (HDC)  wParam ;
				SetBkMode( hdc, TRANSPARENT );
				return DefWindowProc(hDlg,msg,wParam,lParam)+4;				
			}
			else
			{
				return DefWindowProc(hDlg,msg,wParam,lParam);
			}
		}
	case WM_LBUTTONDOWN:
		{
			SetWindowLong(GetParent(hWnd),0,GetWindowLong(hWnd,4));
			EnumChildWindows(GetParent(hWnd), EnumChildRedraw, NULL);
			chosenGraph=(graphClass*)k;
			SendMessage(GetParent(GetParent(hWnd)),WM_USER+102,NULL,NULL);
			break;
		}
	case WM_LBUTTONUP: //actually, there was some way to make Main window handle mouse events of child windows....
		if(mtracking) mtracking=FALSE;
		break;
	case WM_COMMAND:             
		switch(LOWORD(wParam))
		{
		case IDC_BUTTONSCALEPLUS:
			{
				if(!autoScale)
				{
					((graphClass*)k)->setYScale(((graphClass*)k)->getYScale()*2);
					if(chosenGraph==(graphClass*)k) SendMessage(GetParent(GetParent(hWnd)),WM_USER+102,NULL,NULL);
					SendMessage(GetParent(GetParent(hWnd)),WM_USER+100,NULL,NULL);
				}
			}
			break;
		case IDC_BUTTONSCALEMINUS:
			{
				if(!autoScale)
				{
					double sc = ((graphClass*)k)->getYScale();
					sc = ((sc/2)>0)?sc/2:sc;
					((graphClass*)k)->setYScale(sc);
					if(chosenGraph==(graphClass*)k) SendMessage(GetParent(GetParent(hWnd)),WM_USER+102,NULL,NULL);
					SendMessage(GetParent(GetParent(hWnd)),WM_USER+100,NULL,NULL);
				}
			}
			break;
		case IDC_EDITOFFSET:
			{
				switch (HIWORD(wParam))
				{
				case EN_CHANGE:
					{
						//static offTemp;
						HWND grControlDlgEdit = GetDlgItem(hDlg, IDC_EDITOFFSET);
						if (k)
						{
							int yOffset, yOffsetTemp;
							BOOL trCheck = false;
							yOffsetTemp = GetDlgItemInt(hDlg, IDC_EDITOFFSET, &trCheck, TRUE);
							if (trCheck)
							{
								if (abs(yOffsetTemp)>maxGraphOffset)
								{
									if (yOffsetTemp < 0)
									{
										yOffsetTemp = -maxGraphOffset;
										SetWindowText(grControlDlgEdit,L"-9999");
									}
									else
									{
										yOffsetTemp = maxGraphOffset;
										SetWindowText(grControlDlgEdit,L"9999");
									}
								}

								((graphClass*)k)->setYOffset(yOffsetTemp);
								SendMessage(GetParent(GetParent(hWnd)),WM_USER+100,NULL,NULL);
							}
						}
					}
				}
			}
			break;

		case ID_DELETE:
			{
				for (list<graphClass>::iterator ii = graphs.begin();ii!=graphs.end();++ii)
				{
					if(ii->getHCtrl()==((graphClass*)k)->getHCtrl())	
					{
						graphs.erase(ii);
						break;
					}
				}
				HWND mainWnd=GetParent(GetParent(hWnd));
				if (GetWindowLong(GetParent(hWnd),0)==GetWindowLong(hWnd,4))
				{
					SetWindowLong(GetParent(hWnd),0,(LONG)-1);
					chosenGraph = NULL;
					SendMessage(GetParent(GetParent(hWnd)),WM_USER+102,NULL,NULL);
				}
				else if (GetWindowLong(GetParent(hWnd),0)>GetWindowLong(hWnd,4))
				{
					SetWindowLong(GetParent(hWnd),0,(LONG)(GetWindowLong(GetParent(hWnd),0)-1));
				}
				EnumChildWindows(GetParent(hWnd),EnumChildPanelPlacement,(LPARAM)GetWindowLong(hWnd,4));
				scrlinit(GetParent(hWnd));
				SendMessage(GetParent(GetParent(hWnd)),(WM_USER+100),NULL,NULL);
				DestroyWindow(hWnd);				
			}
			break;
		case IDC_CHECKSHOW:
			{
				HWND grControlDlgShow = GetDlgItem(hDlg, IDC_CHECKSHOW);
				if (((graphClass*)k)->isVisible())
				{
					((graphClass*)k)->hide();
					SendMessage(grControlDlgShow, WM_SETTEXT, NULL, (LPARAM)L"Show");
				}
				else
				{
					((graphClass*)k)->show();
					SendMessage(grControlDlgShow, WM_SETTEXT, NULL, (LPARAM)L"Hide");
				}
				SendMessage(GetParent(GetParent(hWnd)),(WM_USER+100),NULL,NULL);                                
			}
			break;
		case IDC_STOPTHIS:
			{
				((graphClass*)k)->stop();
				HWND grControlDlgStop = GetDlgItem(hDlg, IDC_STOPTHIS);
				EnableWindow(grControlDlgStop, FALSE);
			}
			break;
		case IDC_STATIC_Y:
			break;
		case IDC_COLOR:
			{
				CHOOSECOLOR chColor;
				chColor.lStructSize = sizeof(CHOOSECOLOR);
				chColor.hwndOwner = hDlg;
				chColor.lpCustColors = (LPDWORD) custColors;
				chColor.Flags = CC_RGBINIT|CC_FULLOPEN;
				chColor.rgbResult = ((graphClass*)k)->getColor();
				if (ChooseColor(&chColor))
				{
					((graphClass*)k)->setColor(chColor.rgbResult);
					SendMessage(GetParent(GetParent(hWnd)),(WM_USER+100),NULL,NULL);
				}
				//changing color of button
				HWND grControlDlgColor = GetDlgItem(hDlg, IDC_COLOR);				
				RECT drawRect;
				GetClientRect(grControlDlgColor, &drawRect);
				int width=drawRect.right-drawRect.left;
				int height=drawRect.bottom-drawRect.top;
				HDC hDC = GetDC(grControlDlgColor);
				HDC init = CreateCompatibleDC(hDC);
				HBITMAP hBitmap = (HBITMAP)SendMessage(grControlDlgColor, BM_SETIMAGE, IMAGE_BITMAP, NULL);
				HBITMAP oldHBitmap = (HBITMAP)(SelectObject(init, hBitmap));
				HBRUSH brushRect = CreateSolidBrush(((graphClass*)k)->getColor());
				HBRUSH brushFrame = CreateSolidBrush(RGB(0,0,0));

				RECT frameRect = drawRect;
				frameRect.bottom-=difY;
				frameRect.right-=difX;

				FillRect(init, &drawRect, brushRect);
				FrameRect(init, &frameRect, brushFrame);
				SelectObject( init, oldHBitmap );

				SendMessage(grControlDlgColor, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap);
				DeleteObject(brushRect);
				DeleteObject(brushFrame);
				ReleaseDC(grControlDlgColor,hDC);
				DeleteDC(init);
				init = NULL;
			}
			break;
		case IDC_INFO:
			{
				DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG3), hWnd, InfoDlgProc,(LPARAM)k);
			}
			break;
		}
	default:
		return FALSE;
	}
	return FALSE;
}
LRESULT CALLBACK WndProcGraphControl(HWND hWnd,UINT msg,WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
	default:
		return DefWindowProc(hWnd,msg,wParam,lParam);
	}
}

BOOL CALLBACK EnumChildProc(HWND hChild, LPARAM lParam)
{
	LPRECT rcParent;
	int i=0, idChild, width, height,xPosition,yPosition;
	rcParent = (LPRECT) lParam;
	idChild = GetWindowLong(hChild, GWL_ID);

	switch(idChild)
	{
	case ID_DRW_WND:
		{
			RECT drawRect;
			GetWindowRect(hChild, &drawRect);
			width=rcParent->right-rcParent->left-panel_width-yAxisWidth-40;
			height=rcParent->bottom-rcParent->top-bottom_height;
			xPosition = 10+yAxisWidth;
			yPosition = defYInterfaceOffset;    
			SetWindowPos(hChild, HWND_TOP,xPosition, yPosition,width, height,SWP_SHOWWINDOW);                        
			break;
		}
	case ID_PANEL:
		{

			RECT panelRect;
			GetWindowRect(hChild, &panelRect);
			width = panelRect.right - panelRect.left;
			height=rcParent->bottom-rcParent->top-bottom_height;
			xPosition = rcParent->right-panel_width;
			yPosition = defYInterfaceOffset;
			scrlset(hChild);
			SetWindowPos(hChild, HWND_TOP,xPosition, yPosition,width, height,SWP_SHOWWINDOW);
			break;
		}
	case ID_YAXIS:
		{
			RECT drawRect;
			GetWindowRect(hChild, &drawRect);
			width=yAxisWidth;
			height=rcParent->bottom-rcParent->top-bottom_height-xAxisHeight-16;    //not 16, but scrollbar height
			xPosition = 10;                         
			yPosition = defYInterfaceOffset;  
			SetWindowPos(hChild, HWND_TOP,xPosition, yPosition,width, height,SWP_SHOWWINDOW);
			break;
		}            


	case ID_XSCALEMINUS:
		{
			RECT buttonRect;
			GetWindowRect(hChild, &buttonRect);
			width = buttonRect.right - buttonRect.left;
			height = buttonRect.bottom - buttonRect.top;
			xPosition = 10+yAxisWidth;
			yPosition = rcParent->bottom-rcParent->top-70;
			SetWindowPos(hChild, HWND_TOP,xPosition, yPosition,width, height,SWP_SHOWWINDOW);
			break;
		}
	case ID_XSCALEPLUS:
		{
			RECT buttonRect;
			GetWindowRect(hChild, &buttonRect);
			width = buttonRect.right - buttonRect.left;
			height = buttonRect.bottom - buttonRect.top;
			xPosition = 35+yAxisWidth;
			yPosition = rcParent->bottom-rcParent->top-70;
			SetWindowPos(hChild, HWND_TOP,xPosition, yPosition,width, height,SWP_SHOWWINDOW);
			break;
		}
	case ID_XCOORDS:
		{
			RECT buttonRect;
			GetWindowRect(hChild, &buttonRect);
			width = buttonRect.right - buttonRect.left;
			height = buttonRect.bottom - buttonRect.top;
			xPosition = 70+yAxisWidth;
			yPosition = rcParent->bottom-rcParent->top-70;
			SetWindowPos(hChild, HWND_TOP,xPosition, yPosition,width, height,SWP_SHOWWINDOW);
			break;
		}
	}
	int s = idChild;

	return TRUE;
}




LRESULT CALLBACK WndProcYAxis(HWND hWnd,UINT msg,WPARAM wParam, LPARAM lParam)
{
	HWND hWndPar = GetParent(hWnd);

	switch(msg)
	{ 
	case WM_ERASEBKGND:
		return (LRESULT)1;

	case WM_PAINT:
		{
			PAINTSTRUCT ps;
			RECT drawRect;
			GetClientRect(hWnd, &drawRect);
			HDC hDC = BeginPaint(hWnd,&ps);
			int width=drawRect.right-drawRect.left;        
			int height=drawRect.bottom-drawRect.top;

			HDC hDCBuffer = CreateCompatibleDC(hDC);   
			HBITMAP hBitmapBuffer = CreateCompatibleBitmap(hDC,width, height);
			SelectObject(hDCBuffer, hBitmapBuffer);

			//filling background
			LOGBRUSH br;
			br.lbStyle = BS_SOLID;
			br.lbColor = axisBGColor;
			HBRUSH brush;
			brush = CreateBrushIndirect(&br);
			FillRect(hDCBuffer, &drawRect, brush);    
			DeleteObject(brush);

			/////////////////////////////////////////
			if(chosenGraph!=NULL)
			{
				int y0=floor(height/2-chosenGraph->getYOffset()*chosenGraph->getYScale()+0.5);
				int dy=0;    
				int x0=width-strokeLong-4;
				int maxdy=(chosenGraph->getYOffset()*chosenGraph->getYScale()>0)? height-y0: y0;                
				double yscale=chosenGraph->getYScale();
				wstring wstr;
				double val=0;
				double dval=yAxisStep/yscale;    

				double exp10=pow((double)10, floor(log10(dval)-1));
				int tmp=floor(dval/exp10+0.5);        
				if(tmp%5==0) dval=tmp*exp10;
				else dval=floor(dval/exp10/10+0.5)*exp10*10;

				HPEN hPen = CreatePen(PS_SOLID, 1, 0x000000);
				SelectObject( hDCBuffer, hPen );
				SetBkColor(hDCBuffer,axisBGColor);        
				SetTextAlign(hDCBuffer, TA_RIGHT);

				while(dy<=maxdy)
				{                        
					if(dy!=0)
					{
						wstr=toWstring(L"-",val);
						TextOut(hDCBuffer, x0, y0+dy, wstr.c_str(), wstr.size());
						MoveToEx(hDCBuffer,width,y0+dy,NULL);                    
						LineTo(hDCBuffer,width-strokeLong,y0+dy);
					}
					wstr=toWstring(val,L"");
					TextOut(hDCBuffer, x0, y0-dy, wstr.c_str(), wstr.size());
					MoveToEx(hDCBuffer,width,y0-dy,NULL);                    
					LineTo(hDCBuffer,width-strokeLong,y0-dy);
					if (yscale==0) break;

					dy+=yAxisStep;    
					val+=dval;
				}
				if(yscale!=0)
				{
					dy=yAxisStep/2;        //if yAxisStep%2!=0? well, small 1 pix mistake, that's why only 2 div
					while(dy<=maxdy)
					{
						MoveToEx(hDCBuffer,width,y0+dy,NULL);                    
						LineTo(hDCBuffer,width-strokeShort,y0+dy);
						MoveToEx(hDCBuffer,width,y0-dy,NULL);                    
						LineTo(hDCBuffer,width-strokeShort,y0-dy);
						dy+=yAxisStep;
					}
				}
				DeleteObject(hPen);
			}
			/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////            
			SetStretchBltMode(hDC, COLORONCOLOR);
			BitBlt(hDC, 0, 0, width, height, hDCBuffer, 0, 0, SRCCOPY);
			EndPaint(hWnd,&ps);
			DeleteDC(hDCBuffer);
			DeleteObject(hBitmapBuffer);
			hDCBuffer = NULL;    
		}
		return 0;
	default:
		return DefWindowProc(hWnd,msg,wParam,lParam);
	}
	return 0;
}


template< typename T >
std::wstring toWstring(T& val, wstring init, wstring fin) 
{
	std::wstringstream ss;     
	ss << val;
	ss << fin;
	return ss.str();
}
template< typename T >
std::wstring toWstring(T& val, wstring fin)
{
	std::wstringstream ss;      
	ss << val;
	ss << fin;
	return ss.str();
}
template< typename T >
std::wstring toWstring(wstring init, T& val)
{
	std::wstringstream ss;
	ss << init;
	ss << val;        
	return ss.str();
}

void CorrectionXScaleAxis(double& xScale, int& xDiv)        
{            
	double msStep=xScale*xAxisDivStandart*xAxisDivCount;
	int lgg=floor(log10(msStep));
	double exp10=pow((double)10,lgg);    

	if(lgg!=0)        //this condition only for 10 ms - min step!
	{
		msStep=msStep/exp10;

		if(msStep>1 && msStep<=2) msStep=2*exp10;        //only for "round numbers for 10 divisions" logic!  
		else if(msStep>2 && msStep<=2.5) msStep=2.5*exp10;    
		else if(msStep>2.5 && msStep<=5) msStep=5*exp10;    
		else if(msStep>5 && msStep<10) msStep=10*exp10;    
	}
	else
	{
		msStep=10;    //10 ms - min step; should be xAxisDivCount step.    
	}

	if(msStep==1) xDiv=xAxisDivStandart;    //xScale unchanged        
	else
	{
		xDiv=floor(msStep/xScale/xAxisDivCount+0.5);    
		xScale=msStep/(xDiv*xAxisDivCount);        //so both XSCALE and axis step(=10*div) are corrected  
	}        
}


int CorrectionYAxis(const double& yScale)        
{                            
	double valStep=yAxisStepStandart/yScale;    
	int lgg=floor(log10(valStep));
	int yStep;
	double exp10=pow((double)10,lgg);    
	valStep=valStep/exp10;

	if(valStep>1 && valStep<=2) valStep=2*exp10;        //only for "round numbers for 10 divisions" logic!  
	else if(valStep>2 && valStep<=2.5) valStep=2.5*exp10;    
	else if(valStep>2.5 && valStep<=5) valStep=5*exp10;    
	else if(valStep>5 && valStep<10) valStep=10*exp10;        
	if(valStep==1) return yAxisStepStandart;    

	return floor(valStep*yScale+0.5);    
}


HWND ToolbarInit(HWND hWnd, HINSTANCE hInstance)
{
	const int NUMBUTTONS = 6;
	TBBUTTON tbrButtons[NUMBUTTONS];

	tbrButtons[0].idCommand = IDM_SOURCES;
	tbrButtons[0].fsState   = TBSTATE_ENABLED;
	tbrButtons[0].fsStyle   = TBSTYLE_BUTTON;

	tbrButtons[1].idCommand = IDM_CLEARSOURCES;
	tbrButtons[1].fsState   = TBSTATE_INDETERMINATE;
	tbrButtons[1].fsStyle   = TBSTYLE_BUTTON;

	tbrButtons[2].idCommand = 0;
	tbrButtons[2].fsState   = TBSTATE_ENABLED;
	tbrButtons[2].fsStyle   = TBSTYLE_SEP;

	tbrButtons[3].idCommand =ID_PLAYING_AUTOSCALING;
	tbrButtons[3].fsState   = TBSTATE_ENABLED | TBSTATE_CHECKED;
	tbrButtons[3].fsStyle   = TBSTYLE_CHECK;	

	tbrButtons[4].idCommand = ID_PLAYING_AUTOSCROLLING;
	tbrButtons[4].fsState   = TBSTATE_ENABLED | TBSTATE_CHECKED;
	tbrButtons[4].fsStyle   = TBSTYLE_CHECK;

	tbrButtons[5].idCommand = ID_PLAYING_STOPALL;
	tbrButtons[5].fsState   = TBSTATE_INDETERMINATE;
	tbrButtons[5].fsStyle   = TBSTYLE_BUTTON;

	for(int i=0;i<NUMBUTTONS; ++i)
	{
		if (tbrButtons[i].fsStyle!=TBSTYLE_SEP) tbrButtons[i].iBitmap = i;
		else tbrButtons[i].iBitmap=20;	//size for separator
		tbrButtons[i].dwData    = 0L;
		tbrButtons[i].iString   = 0;
	}

	HWND hWndToolbar;
	hWndToolbar = CreateToolbarEx(hWnd, WS_VISIBLE | WS_CHILD | WS_BORDER | TBSTYLE_TOOLTIPS | WS_CLIPCHILDREN, IDB_TOOLBAR, NUMBUTTONS,
		hInstance,IDB_TOOLBAR,tbrButtons, NUMBUTTONS, 16, 16, 16, 16,sizeof(TBBUTTON));
	
	return hWndToolbar;
}


void SetIcon(HWND hWnd, HINSTANCE hInstance, WORD id)
{
	HICON hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(id));
	SetClassLong(hWnd, GCL_HICON, (LONG)hIcon);
	hIcon = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(id), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);	
	SetClassLong(hWnd, GCL_HICONSM, (LONG)hIcon);
}
